package smallfrench.moteur.noeuds;

import java.util.ArrayList;

import smallfrench.moteur.biblio.AClasse;
import smallfrench.moteur.biblio.err.AException;
import smallfrench.moteur.biblio.err.ErreurClasse;
import smallfrench.moteur.biblio.err.ErreurRien;
import smallfrench.moteur.interp.Portee;
import smallfrench.moteur.interp.RObjet;
import smallfrench.moteur.interp.err.RException;
import smallfrench.moteur.lexer.lexemes.Lexeme;

public class NEssayer extends Noeud {

	/** Le nœud susceptible de lancer une exception */
	private final Noeud noeudEssai;

	/** Les nœuds des "siexception" */
	private final NCatch[] noeudsCatch;

	//-------------------------------------------------------------------------

	public NEssayer(Lexeme tokenEssayer, Noeud noeudEssai,
			ArrayList<NCatch> noeudsSiexception) {

		super(tokenEssayer);

		this.noeudEssai = noeudEssai;

		if (noeudsSiexception.size() == 0)
			throw new IllegalArgumentException("Aucun nœud siexception");

		this.noeudsCatch = noeudsSiexception.toArray(
				new NCatch[noeudsSiexception.size()]);
	}

	//-------------------------------------------------------------------------

	@Override
	public RObjet evaluer(Portee portee) {

		try {

			return noeudEssai.evaluer(portee);

		} catch (RException re) {

			for (NCatch nCatch : noeudsCatch) {

				Noeud noeudClasseException = nCatch.noeudClasseException;

				RObjet o = noeudClasseException.evaluer(portee);

				if (o == null) {
					throw ErreurRien.nouvelleException(
							noeudClasseException.lexeme.debut);
				}

				if (! (o instanceof AClasse)) {
					throw ErreurClasse.nouvelleException(
							noeudClasseException.lexeme.debut,
							o, AException.CLASSE);
				}

				AClasse classe = (AClasse) o;

				if (re.estUne(classe)) {

					// Le type de l’exception correspond, on évalue
					// l’instruction
					Noeud noeud = nCatch.noeudInstruction;

					// On ajoute la variable de l’exception
					portee.ajouterVariable(
							nCatch.nomVariableException, re.aObjet);

					return noeud.evaluer(portee);
				}
			}

			// On n’a pas trouvé de catch avec la bonne classe d’exception
			throw re;
		}
	}

	@Override
	public String toString() {
		String s = "essayer " + noeudEssai + " ";
		for (NCatch element : noeudsCatch) {
			s += element + " ";
		}
		return s;
	}

	//-------------------------------------------------------------------------

	/**
	 * Un noeud similaire à un <code>catch</code> Java ; pour attrapper une
	 * certaine classe d’exception.
	 */
	public final static class NCatch {

		/** Le noeud de la classe d’exception attendue */
		public final Noeud noeudClasseException;

		/** Le nom de la variable pointant vers l’instance de l’exception
		 * attrappée */
		public final String nomVariableException;

		/** Le noeud de l’instruction à évaluer */
		public final Noeud noeudInstruction;

		public NCatch(Noeud noeudClasseException,
				String nomVariableException,
				Noeud noeudInstruction) {

			this.noeudClasseException = noeudClasseException;
			this.noeudInstruction = noeudInstruction;
			this.nomVariableException = nomVariableException;
		}

		@Override
		public String toString() {
			return "siexception " + nomVariableException + " (" +
					noeudClasseException + ") " + noeudInstruction;
		}
	}
}
